package com.siwuxie095.functional.chapter6th.example7th;

import java.util.Arrays;
import java.util.List;

/**
 * @author Jiajing Li
 * @date 2020-10-24 16:10:12
 */
@SuppressWarnings("all")
public class Main {

    /**
     * 性能
     *
     * 影响并行流性能的主要因素有 5 个，如下：
     *
     * （1）数据大小
     *
     * 输入数据的大小会影响并行化处理对性能的提升。将问题分解之后并行化处理，再将结果合并会带来额外的开销。因此只有数据
     * 足够大、每个数据处理管道花费的时间足够多时，并行化处理才有意义。
     *
     * （2）源数据结构
     *
     * 每个管道的操作都基于一些初始数据源，通常是集合。将不同的数据源分割相对容易，这里的开销影响了在管道中并行处理数据
     * 时到底能带来多少性能上的提升。
     *
     * （3）装箱
     *
     * 处理基本类型比处理装箱类型要快。
     *
     * （4）核的数量
     *
     * 在极端情况下，只有一个核，因此完全没必要并行化。显然，拥有的核越多，获得潜在性能提升的幅度就越大。在实践中，核的
     * 数量不单指你的机器上有多少核，更是指运行时机器能使用多少核。这也就是说同时运行的其他进程，或线程关联性（强制线程
     * 在某些核或 CPU 上运行）会影响性能。
     *
     * （5）单元处理开销
     *
     * 比如数据大小，这是一场并行执行花费时间和分解合并操作开销之间的战争。花在流中每个元素身上的时间越长，并行操作带来
     * 的性能提升越明显。
     *
     *
     * 了解这些因素后，就能理解哪些能工作、哪些不能工作，且能帮助在如何使用、以及什么时候使用并行流这一问题上做出明智的
     * 决策。
     *
     * 使用并行流框架，对于理解如何分解和合并问题是很有帮助的。这让程序员能够知悉底层如何工作，但却不必了解框架的细节。
     *
     * 来看一个具体的问题，看看如何分解和合并它。如下：
     *
     *     private static int addIntegers(List<Integer> values) {
     *         return values.parallelStream()
     *             .mapToInt(i -> i)
     *             .sum();
     *     }
     *
     * 在底层，并行流还是沿用了 fork/join 框架。fork 递归式地分解问题，然后每段并行执行，最终由 join 合并结果，返回
     * 最后的值。
     *
     * 假设并行流将上述问题中的工作分解开，在一个四核的机器上并行执行。
     *
     * （1）数据被分成四块。
     * （2）计算工作在每个线程里并行执行。这包括将每个 Integer 对象映射为 int 值，然后在每个线程里将 1/4 的数字相加。
     * 理想情况下，希望在这里花的时间越多越好，因为这里是并行操作的最佳场所。
     * （3）然后合并结果。在本例中就是 sum 操作，但这也可能是 reduce、collect 或其他终结操作。
     *
     * 可以根据性能的好坏，将核心类库提供的通用数据结构分成以下 3 组。
     *
     * （1）性能好
     *
     * ArrayList、数组或 IntStream.range，这些数据结构支持随机读取，也就是说它们能轻而易举地被任意分解。
     *
     * （2）性能一般
     *
     * HashSet、TreeSet，这些数据结构不易公平地被分解，但是大多数时候分解是可能的。
     *
     * （3）性能差
     *
     * 有些数据结构难于分解，比如，可能要花 O(N) 的时间复杂度来分解问题。其中包括 LinkedList，对半分解太难了。还有
     * Streams.iterate 和 BufferedReader.lines，它们长度未知，因此很难预测该在哪里分解。
     *
     *
     * 初始的数据结构影响巨大。举一个极端的例子，对 10 000 个整数并行求和，使用 ArrayList 要比使用 LinkedList 快
     * 10 倍。这不是说业务逻辑的性能情况也会如此，只是说明了数据结构对于性能的影响之大。使用形如 LinkedList 这样难于
     * 分解的数据结构并行运行可能更慢。
     *
     * 理想情况下，一旦流框架将问题分解成小块，就可以在每个线程里单独处理每一小块，线程之间不再需要进一步通信。无奈现实
     * 不总遂人愿！
     *
     * 在讨论流中单独操作每一块的种类时，可以分成两种不同的操作：无状态的和有状态的。无状态操作整个过程中不必维护状态，
     * 有状态操作则有维护状态所需的开销和限制。
     *
     * 如果能避开有状态，选用无状态操作，就能获得更好的并行性能。无状态操作包括 map、 filter 和 flatMap，有状态操作
     * 包括 sorted、distinct 和 limit。
     */
    public static void main(String[] args) {
        addIntegers(Arrays.asList(1, 2, 3));
    }

    private static int addIntegers(List<Integer> values) {
        return values.parallelStream()
            .mapToInt(i -> i)
            .sum();
    }


}
